﻿using System;
using System.Threading.Tasks;
using System.Windows.Input;

namespace SearchScreen.SearchScreenModule.Commands
{
    public class AsyncDelegateCommand<T> : ICommand
    {
        protected Func<T, Task> _asyncExecute;
        protected readonly Predicate<T> _canExecute;
        protected IConcurrencyGroup _reentryGroup;

        public AsyncDelegateCommand(Func<T, Task> asyncExecute,
            Predicate<T> canExecute = null,
            IConcurrencyGroup reentryGroup = null)
        {
            this._asyncExecute = asyncExecute;
            this._canExecute = canExecute;
            this._reentryGroup = reentryGroup;
            if (null != _reentryGroup)
            {
                this._reentryGroup.CanExecuteChanged += (o, e) => RaiseCanExecuteChange();
            }
        }


        public bool CanExecute(object parameter)
        {
            if (null != this._reentryGroup && !this._reentryGroup.CanExecute())
            {
                return false;
            }

            if (_canExecute == null)
            {
                return true;
            }

            return _canExecute((T)parameter);
        }

        public event EventHandler CanExecuteChanged;

        public void Execute(object parameter)
        {
            ExecuteAsync((T)parameter);
        }

        protected virtual Task ExecuteAsync(T parameter)
        {
            if (null != _reentryGroup)
            {
                using (_reentryGroup.GetExcutionScope())
                {
                    return _asyncExecute(parameter);
                }
            }
            else
            {
                return _asyncExecute(parameter);
            }
        }

        protected virtual void RaiseCanExecuteChange()
        {
            var handler = this.CanExecuteChanged;
            if (null != handler)
            {
                handler(this, EventArgs.Empty);
            }
        }
    }

}
